<?php

CONST READ_BYTES = 10000;
CONST WORKER_URL = "http://localhost/worker.php";

CONST CONNECTON_TIMEOUT = 30;
CONST STREAM_TIMEOUT = 86400;

require_once 'SerializableClosure.php';

class Thread {
	/**
	 * The actual SerializableClosure function we'll be running on a seperate "thread".
	 * 
	 * The reason why we wrap the Closure in a SerializableClosure object is to achieve serializability. 
	 * Closures cannot be serialized by default.
	 * 
	 * @var SerializableClosure
	 */
	private $closure;
	/**
	 * Construct the Thread object
	 * @param Closure $closure - the actual closure object we'll be running
	 */
	public function __construct(Closure $closure) {
		$this->closure = new SerializableClosure($closure);
	}
	/**
	 * The pointer to the stream we'll be reading data from (Worker)
	 * @var resource
	 */
	private $pointer = EOF;
	/**
	 * This variable keeps the data recieved from the Worker
	 * @var String
	 */
	private $data = '';
	/**
	 * Start the closure on another thread. 
	 * This is a non-blocking operation, meaning we wont wait for a responce. 
	 */
	public function run() {

		$errno = '';
		$errstr = '';
	
		set_time_limit(0);
		$this->pointer = fsockopen($_SERVER["SERVER_NAME"], SERVER_PORT, $errno, $errstr, CONNECTON_TIMEOUT);
		if (!$this->pointer) {
		   throw new Exception("Unable to create file pointer, error(". $errno . "): " . $errstr);
		}
		$out = "GET ".WORKER_URL."HTTP/1.1\r\n";
		$out .= "Host: ".$_SERVER["SERVER_NAME"]."\r\n";
		$out .= "Connection: Close\r\n\r\n";
		
		stream_set_blocking($this->pointer, false);
		stream_set_timeout($this->pointer, STREAM_TIMEOUT);
		fwrite($this->pointer, $out);
		
	}
	/**
	 * Determines if the thread is currently busy or not.
	 */
	public function isBusy() {
		if(feof($this->pointer)) {
			fclose($this->pointer);
			return false;
		}
		else {
			$this->data .= fread($this->pointer, READ_BYTES);
			return true;
		}
	}
	/**
	 * Gets the results of this thread. Will return an empty string if there is no output.
	 */
	public function getResults() {
		if($this->isBusy())
			throw new Exception("Cannot return results of a thread that is currently busy");
		
		return $this->data;
	}
}